using System;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using Microsoft.Deployment.WindowsInstaller;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading;
using System.Diagnostics;
using Microsoft.Win32;

namespace CustomAction
{
    /// <summary>
    /// Класс пользовательских действий.
    /// </summary>
    public class CustomActions
    {
        /// <summary>
        /// Производит подгрузку файлов из заданного ресурса.
        /// </summary>
        /// <param name="session">Сессия WIX.</param>
        /// <returns>Возвращает статус завершения действия.</returns>
        [CustomAction]
        public static ActionResult ContinueInstall(Session session)
        {
            var wixWindow = Utils.GetWixWindowHandle(session.CustomActionData["ProductName"]);

            if (!Utils.IsAdmin())
            {
                MessageBox.Show(wixWindow, "Ошибка: Необходимо запустить установку от имени администратора.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);

                throw new InstallerException("Ошибка: нет прав администратора.");
            }

            using (var form = new FormContinueInstall(
                session.CustomActionData["ResourceForUpdatePath"],
                session.CustomActionData["InstallDir"],
                session.CustomActionData["PureFolders"].Split(';'),
                session.CustomActionData["ExcludeFolders"].Split(';'),
                session.CustomActionData["ExcludeFiles"].Split(';'),
                Convert.ToBoolean(session.CustomActionData["FullMatchDirectories"]),
                session.CustomActionData["CheckSumFile"]))
            {
                switch (form.ShowDialog(wixWindow))
                {
                    case DialogResult.Cancel:
                        throw new InstallCanceledException("Операция отменена.");

                    case DialogResult.Abort:
                        throw new InstallerException("Операция завершена с ошибкой.");  
                }
            }

            return ActionResult.Success;
        }

        /// <summary>
        /// Производит установку прав полного доступагруппы "Прошедшие проверку" для директории установки программы.
        /// </summary>
        /// <param name="session">Сессия WIX.</param>
        /// <returns>Возвращает статус завершения действия.</returns>
        [CustomAction]
        public static ActionResult SetPermissionForInstallDir(Session session)
        {
            DirectoryInfo installDir = new DirectoryInfo(session.CustomActionData["InstallDir"]);
            DirectorySecurity dirSecurity = installDir.GetAccessControl();

            try
            {
                dirSecurity.AddAccessRule(
                    new FileSystemAccessRule(
                        new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null),
                        FileSystemRights.FullControl,
                        InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit,
                        PropagationFlags.None,
                        AccessControlType.Allow));

                installDir.SetAccessControl(dirSecurity);
            }
            catch (Exception ex)
            {
                throw new InstallerException("Ошибка при удалении установке прав доступа к директории.", ex);
            }

            return ActionResult.Success;
        }

        /// <summary>
        /// Удаление установочной папки, а также папки компании если она пуста.
        /// </summary>
        /// <param name="session">Сессия WIX.</param>
        /// <returns>Возвращает статус завершения действия.</returns>
        [CustomAction]
        public static ActionResult DeleteInstallDir(Session session)
        {
            if (!Utils.IsAdmin())
            {
                MessageBox.Show("Ошибка: Необходимо запустить удаление от имени администратора.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);

                throw new InstallerException("Ошибка: нет прав администратора.");
            }

            try
            {
                Directory.Delete(session.CustomActionData["InstallDir"], true);

                // Если папка компании пуста, удаляем и ее
                if (!Directory.EnumerateFileSystemEntries(session.CustomActionData["ManufacturerDir"]).Any())
                {
                    Directory.Delete(session.CustomActionData["ManufacturerDir"]);
                }
            }
            catch (Exception ex)
            {
                throw new InstallerException("Ошибка при удалении директории с программой.", ex);
            }

            return ActionResult.Success;
        }

        /// <summary>
        /// Выполняет проверку зависимостей.
        /// </summary>
        /// <param name="session">Сессия WIX.</param>
        /// <returns>Возвращает результат операции.</returns>
        [CustomAction]
        public static ActionResult CheckDependence(Session session)
        {
            var wixWindow = Utils.GetWixWindowHandle(session["ProductName"]);

            // Microsoft SQL Server 2012.
            var msSql = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Microsoft SQL Server\110\Tools\ClientSetup\CurrentVersion");

            if (msSql == null || msSql.GetValue("CurrentVersion") == null)
            {
                Utils.ShowErrorBox("Установка невозможна, так как отсутствует Microsoft SQL Server 2012. Выполните установку и повторите попытку.", wixWindow);
                return ActionResult.Failure;
            }

            // Microsoft SQL Server Compact 3.5 Service Pack 2
            var msSqlCompact = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Microsoft SQL Server Compact Edition\v3.5\ENU");
            var msSqlCompactVersionValue = msSqlCompact.GetValue("DesktopRuntimeVersion");
            var msSqlCompactServicePachLevel = msSqlCompact.GetValue("DesktopRuntimeServicePackLevel");

            if (msSqlCompact == null || msSqlCompactVersionValue == null || msSqlCompactServicePachLevel == null ||
                (msSqlCompactVersionValue as string).Substring(0, 3) != "3.5" || System.Convert.ToInt32(msSqlCompactServicePachLevel) < 2)
            {
                Utils.ShowErrorBox("Установка невозможна, так как отсутствует Microsoft SQL Server Compact 3.5 Service Pack 2. Выполните установку и повторите попытку.", wixWindow);
                return ActionResult.Failure;
            }

            // Microsoft SQL Server Management Objects
            var msSmo = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Microsoft SQL Server\SharedManagementObjects\CurrentVersion");
            if (msSmo == null || msSmo.GetValue("Version") == null)
            {
                Utils.ShowErrorBox("Установка невозможна, так как отсутствует Microsoft SQL Server Management Objects. Выполните установку и повторите попытку.", wixWindow);
                return ActionResult.Failure;
            }

            return ActionResult.Success;
        }

        /// <summary>
        /// Подготовка к запуску.
        /// </summary>
        /// <param name="session">Сессия WIX.</param>
        /// <returns>Возвращает результат операции.</returns>
        [CustomAction]
        public static ActionResult PreparingToRun(Session session)
        {
            // Проверяем не запущен ли еще установщик
            if (Utils.GetHandlesThisProject(session["ProductName"]).Length > 1)
            {
                Utils.ShowErrorBox("Установщик уже запущен.");
                return ActionResult.Failure;
            }

            // Если не передали параметр для ожидания завершения программы, то выходим.
            if (session["PID"].Length == 0)
            {
                return ActionResult.Success;
            }
           
            // Дожидаемся завершения процесса программы, если истекает TimeOut закрываем принудительно.
            Process process = null;
            int processId;

            if (!int.TryParse(session["PID"], out processId))
            {
                throw new ArgumentException("Ошибка парсинга параметра идентификатора процесса.");
            }

            try
            {
                process = Process.GetProcessById(processId);
            }
            catch (Exception ex)
            {
                throw new Exception("Не найден процесс закрытие которого требуется ожидать.", ex);
            }
            
            if (process != null && !process.WaitForExit(5 * 1000))
            {
                process.Kill();
            }

            return ActionResult.Success;
        }
    }
}
